1   /*
2    * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   * @test
26   * @bug 4919632
27   * @summary Unit test for ISO8601 time zone format support
28   */
29  
30  import java.text.*;
31  import java.util.*;
32  
33  public class ISO8601ZoneTest {
34      static final Date TIMESTAMP = new Date(1283758039020L);
35  
36      static final String[][] formatData = {
37          // time zone name, expected output at TIMESTAMP
38          { "America/Los_Angeles", "2010-09-06T00:27:19.020-07", },
39          { "America/Los_Angeles", "2010-09-06T00:27:19.020-0700", },
40          { "America/Los_Angeles", "2010-09-06T00:27:19.020-07:00", },
41          { "Australia/Sydney", "2010-09-06T17:27:19.020+10", },
42          { "Australia/Sydney", "2010-09-06T17:27:19.020+1000", },
43          { "Australia/Sydney", "2010-09-06T17:27:19.020+10:00", },
44          { "GMT-07:00", "2010-09-06T00:27:19.020-07", },
45          { "GMT-07:00", "2010-09-06T00:27:19.020-0700", },
46          { "GMT-07:00", "2010-09-06T00:27:19.020-07:00", },
47          { "UTC", "2010-09-06T07:27:19.020Z", },
48          { "UTC", "2010-09-06T07:27:19.020Z", },
49          { "UTC", "2010-09-06T07:27:19.020Z", },
50      };
51  
52      static final String[] zones = {
53          "America/Los_Angeles", "Australia/Sydney", "GMT-07:00",
54          "UTC", "GMT+05:30", "GMT-01:23",
55      };
56  
57      static final String[] isoZoneFormats = {
58          "yyyy-MM-dd'T'HH:mm:ss.SSSX",
59          "yyyy-MM-dd'T'HH:mm:ss.SSSXX",
60          "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
61      };
62  
63      // badData[][0] - format
64      // badData[][1] - (bad) text to be parsed
65      // badData[][2] - subtext at the end of which a parse error is detected
66      static final String[][] badData = {
67          { "X", "1", "1" },
68          { "X", "+1", "+1" },
69          { "X", "-2", "-2" },
70          { "X", "-24", "-2" },
71          { "X", "+24", "+2" },
72  
73          { "XX", "9", "9" },
74          { "XX", "23", "2" },
75          { "XX", "234", "2" },
76          { "XX", "3456", "3" },
77          { "XX", "23456", "2" },
78          { "XX", "+1", "+1" },
79          { "XX", "-12", "-12" },
80          { "XX", "+123", "+123" },
81          { "XX", "-12:34", "-12" },
82          { "XX", "+12:34", "+12" },
83          { "XX", "-2423", "-2" },
84          { "XX", "+2423", "+2" },
85          { "XX", "-1260", "-126" },
86          { "XX", "+1260", "+126" },
87  
88          { "XXX", "9", "9" },
89          { "XXX", "23", "2" },
90          { "XXX", "234", "2" },
91          { "XXX", "3456", "3" },
92          { "XXX", "23456", "2" },
93          { "XXX", "2:34", "2" },
94          { "XXX", "12:4", "1" },
95          { "XXX", "12:34", "1" },
96          { "XXX", "-1", "-1" },
97          { "XXX", "+1", "+1" },
98          { "XXX", "-12", "-12" },
99          { "XXX", "+12", "+12" },
100         { "XXX", "-123", "-12" },
101         { "XXX", "+123", "+12" },
102         { "XXX", "-1234", "-12" },
103         { "XXX", "+1234", "+12" },
104         { "XXX", "+24:23", "+2" },
105         { "XXX", "+12:60", "+12:6" },
106         { "XXX", "+1:23", "+1" },
107         { "XXX", "+12:3", "+12:3" },
108     };
109 
110     static String[] badFormats = {
111         "XXXX", "XXXXX", "XXXXXX",
112     };
113 
114     public static void main(String[] args) throws Exception {
115         TimeZone tz = TimeZone.getDefault();
116         Locale loc = Locale.getDefault();
117         Locale.setDefault(Locale.US);
118 
119         try {
120             for (int i = 0; i < formatData.length; i++) {
121                 TimeZone.setDefault(TimeZone.getTimeZone(formatData[i][0]));
122                 formatTest(isoZoneFormats[i % isoZoneFormats.length],
123                            formatData[i][1]);
124             }
125 
126             for (String zone : zones) {
127                 TimeZone.setDefault(TimeZone.getTimeZone(zone));
128                 for (String fmt : isoZoneFormats) {
129                     roundtripTest(fmt);
130                     SimpleDateFormat f = new SimpleDateFormat(fmt);
131                 }
132 
133             }
134 
135             for (String[] d : badData) {
136                 badDataParsing(d[0], d[1], d[2].length());
137             }
138 
139             for (String fmt : badFormats) {
140                 badFormat(fmt);
141             }
142         } finally {
143             TimeZone.setDefault(tz);
144             Locale.setDefault(loc);
145         }
146 
147     }
148 
149     static void formatTest(String fmt, String expected) throws Exception {
150         SimpleDateFormat sdf = new SimpleDateFormat(fmt);
151         String s = sdf.format(TIMESTAMP);
152         if (!expected.equals(s)) {
153             throw new RuntimeException("formatTest: got " + s
154                                        + ", expected " + expected);
155         }
156 
157         Date d = sdf.parse(s);
158         if (d.getTime() != TIMESTAMP.getTime()) {
159             throw new RuntimeException("formatTest: parse(" + s
160                                        + "), got " + d.getTime()
161                                        + ", expected " + TIMESTAMP.getTime());
162         }
163 
164         ParsePosition pos = new ParsePosition(0);
165         d = sdf.parse(s + "123", pos);
166         if (d.getTime() != TIMESTAMP.getTime()) {
167             throw new RuntimeException("formatTest: parse(" + s
168                                        + "), got " + d.getTime()
169                                        + ", expected " + TIMESTAMP.getTime());
170         }
171         if (pos.getIndex() != s.length()) {
172             throw new RuntimeException("formatTest: wrong resulting parse position: "
173                                        + pos.getIndex() + ", expected " + s.length());
174         }
175     }
176 
177     static void roundtripTest(String fmt) throws Exception {
178         SimpleDateFormat sdf = new SimpleDateFormat(fmt);
179         Date date = new Date();
180 
181         int fractionalHour = sdf.getTimeZone().getOffset(date.getTime());
182         fractionalHour %= 3600000; // fraction of hour
183 
184         String s = sdf.format(date);
185         Date pd = sdf.parse(s);
186         long diffsInMillis = pd.getTime() - date.getTime();
187         if (diffsInMillis != 0) {
188             if (diffsInMillis != fractionalHour) {
189                 throw new RuntimeException("fmt= " + fmt
190                                            + ", diff="+diffsInMillis
191                                            + ", fraction=" + fractionalHour);
192             }
193         }
194     }
195 
196 
197     static void badDataParsing(String fmt, String text, int expectedErrorIndex) {
198         SimpleDateFormat sdf = new SimpleDateFormat(fmt);
199         try {
200             sdf.parse(text);
201             throw new RuntimeException("didn't throw an exception: fmt=" + fmt
202                                        + ", text=" + text);
203         } catch (ParseException e) {
204             // OK
205         }
206 
207         ParsePosition pos = new ParsePosition(0);
208         Date d = sdf.parse(text, pos);
209         int errorIndex = pos.getErrorIndex();
210         if (d != null || errorIndex != expectedErrorIndex) {
211             throw new RuntimeException("Bad error index=" + errorIndex
212                                        + ", expected=" + expectedErrorIndex
213                                        + ", fmt=" + fmt + ", text=" + text);
214         }
215     }
216 
217     static void badFormat(String fmt) {
218         try {
219             SimpleDateFormat sdf = new SimpleDateFormat(fmt);
220             throw new RuntimeException("Constructor didn't throw an exception: fmt=" + fmt);
221         } catch (IllegalArgumentException e) {
222             // OK
223         }
224         try {
225             SimpleDateFormat sdf = new SimpleDateFormat();
226             sdf.applyPattern(fmt);
227             throw new RuntimeException("applyPattern didn't throw an exception: fmt=" + fmt);
228         } catch (IllegalArgumentException e) {
229             // OK
230         }
231     }
232 }